#region Imported Namespaces
using System;
using System.Data;
using System.Data.SqlClient;

using BO = PHSRAG.Insight21.BusinessObjects;
using Utility = PHSRAG.Utility;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using Security = PHSRAG.Security;
using GenerateKey = PHSRAG.Insight21.BusinessObjects.ModuleKey;
using PHSRAG.Insight21.Admin;
using PHSRAG.Utility;
#endregion

namespace PHSRAG.Insight21.Context
{
	#region Class UserContext
	/// <summary>
	/// The UserContext class represents aspects of a logged-in user that needs to be aceesible within the entire
	/// application. The lifetime of such user context is a session.
	/// </summary>
	public class UserContext : BO.SaveableBusinessObject
	{
		#region Constants
		public static string UserContextKey = GenerateKey.MakeKey("UserContext_F473A37E-9EDE-413a-BB82-ADA46021ADBB",GenerateKey.Common);
		#endregion

		#region Instance Variables
		private SqlCommand	addCommand;
		private string		domainUserName;
		private SqlCommand	updateCommand;
        private User        user;
		#endregion

		#region Constants
		//DataTable Name
		private const string PIListTableName = "PIList";
		private const string UserContextTableName = "UserContext";
		private const string KeyGiverOrganizationsTableName = "KeyGiverOrganizations";
		private const string KeyGiverRightsTableName = "KeyGiverRights";
		private const string OrganizationAccessCountTableName = "OrganizationAccessCount";
		private const string CurrentSessionHistoryTableName = "CurrentSessionHistory";
		private const string SessionHistoryTableName = "SessionHistory";
		private const string RightsBitmask = "RightBitmask";

		//UserContext Column Names
		private const string IsActiveBitMaskColName = "IsActiveBitMask";
		private const string ModuleBitmaskColName = "ModuleBitmask";
		private const string ModuleRoleBitmaskColName = "ModuleRoleBitmask";
		private const string ApplicationUserIdColName = "ApplicationUserId";
		private const string DomainUserIdColName = "DomainUserId";
		private const string UsernameColName = "Username";
		private const string FirstNameColName = "FirstName";
		private const string LastNameColName = "LastName";
		private const string PersonIdColName = "AdmPersonId";
		private const string EmailAddressColName = "EmailAddress";
		private const string PIInstitution = "Institution";

		//UserContext Column Names
		private const string SessionHistoryIdColName = "SessionHistoryId";
		private const string SessionApplicationUserIdColName = "SessionApplicationUserId";
		private const string SessionEndDateColName = "SessionEndDate";
		private const string SessionStartDateColName = "SessionStartDate";

		//FwkDomainUser.Username column size
		public const int ColumnSizeUserName = 36;
		#endregion

		#region Constructor
		/// <summary>
		/// Initialize the state of this object.
		/// </summary>
		public UserContext(ICachePolicy cachePolicy) : base(cachePolicy)
		{
			DataContainer = new DataSet();
			CreateCommands();
		}
		#endregion 

		#region Properties
		/// <summary>
		/// Get the key to be used in all caching operations for this instance of the object.
		/// </summary>
		public static string CacheKey	{ get { return UserContextKey; } }

        public User User
        {
            get
            {
                if (user == null)
                {
                    user = new User(CachePolicy);
                    user.Load(DataAccess.ConnectionString, PersonId);
                }
                return user;
            }
        }

		/// <summary>
		/// IsActive indicates if the application user is active
		/// </summary>
		public int IsActiveBitMask 
		{ 
			get 
			{ 
				return (int)DataContainer.Tables[UserContextTableName].Rows[0][IsActiveBitMaskColName];
			}	
		}
		/// <summary>
		/// Checks if the logged in user is a keygiver. It should be noted that this property
		/// also loads the keygiver table to the datacontainer if not already loaded.
		/// </summary>
		public bool IsKeyGiver 
		{
			get 
			{
				if ( DataContainer.Tables[KeyGiverOrganizationsTableName] == null ) 
				{
					Utility.DataAccess.LoadDataSet(DataContainer, KeyGiverOrganizationsTableName, Utility.DataAccess.GetConnectionString(), "GetKeyGiverOrganizationList", 
						Utility.DataAccess.BuildSqlParameterArray("@domainUserID", SqlDbType.Int, DomainUserId));

					if ( !DataContainer.Tables.Contains(KeyGiverOrganizationsTableName) )
						throw new Exception("Failed to load key giver information into user context.");

				}

				if ( DataContainer.Tables[KeyGiverRightsTableName] == null ) 
				{
					Utility.DataAccess.LoadDataSet(DataContainer, KeyGiverRightsTableName, Utility.DataAccess.GetConnectionString(), "GetKeyGiverRights", 
						Utility.DataAccess.BuildSqlParameterArray("@keyGiverDomainUserID", SqlDbType.Int, ((ModuleRoleBitmask & (int)Security.Enumerations.ModuleRoleBit.SystemAdministrator) != 0) ? 0 : DomainUserId ));

					if ( !DataContainer.Tables.Contains(KeyGiverRightsTableName) )
						throw new Exception("Failed to load key giver information into user context.");
				}

				Cache();

				return (DataContainer.Tables[KeyGiverOrganizationsTableName].Rows.Count != 0);
			}
		}

		/// <summary>
		/// Gets the boolean value indicating whether or not the user is a system administrator.
		/// </summary>
		public bool IsUserSysAdmin
		{
			get
			{
				return ((ModuleRoleBitmask & (int)Security.Enumerations.ModuleRoleBit.SystemAdministrator) != 0);
			}
		}

		/// <summary>
		/// Gets the keygiver's organization datatable including ancester hierarchy.
		/// </summary>
		public DataTable KeyGiverOrganizations
		{
			get 
			{
				return IsKeyGiver ? DataContainer.Tables[KeyGiverOrganizationsTableName] : null;
			}
		}

		/// <summary>
		/// Gets the keygiver's organization details datatable.
		/// </summary>
		public DataTable KeyGiverRights
		{
			get 
			{
				return (IsKeyGiver || (ModuleRoleBitmask & (int)Security.Enumerations.ModuleRoleBit.SystemAdministrator) != 0) ? 
					DataContainer.Tables[KeyGiverRightsTableName] : null;
			}
		}

		/// <summary>
		/// ModuleRoleBitmask is the sum of all module role bitvalues that the user has
		/// been granted.  Roles allow access to functionality within the 
		/// application.
		/// </summary>
		public int ModuleRoleBitmask 
		{ 
			get 
			{ 
				return (int)DataContainer.Tables[UserContextTableName].Rows[0][ModuleRoleBitmaskColName];
			}	
		}
		
		/// <summary>
		/// ModuleBitmask is the sum of all module bitvalues that the user has
		/// been granted.  Modules allow access to functionality within the 
		/// application.
		/// </summary>
		public int ModuleBitmask 
		{ 
			get 
			{ 
				return (int)DataContainer.Tables[UserContextTableName].Rows[0][ModuleBitmaskColName];
			}	
		}

		/// <summary>
		/// RightBitmask is the sum of all rights bitvalues that the user has
		/// been granted on the portfolio level.  Rights allow access to the controls on the pages.
		/// </summary>
		public int RightBitmask 
		{ 
			get 
			{ 
				if ( DataContainer.Tables[RightsBitmask] == null ) 
				{
					Utility.DataAccess.LoadDataSet(DataContainer, RightsBitmask, Utility.DataAccess.GetConnectionString(), "GetUserAccessToPortfolio", 
						Utility.DataAccess.BuildSqlParameterArray("@domainUserID", SqlDbType.Int, DomainUserId));

					if ( !DataContainer.Tables.Contains(RightsBitmask) )
						throw new Exception("Failed to load portfolio access for the user");

				}

				Cache();				

				return (int)((DataContainer.Tables[RightsBitmask].Rows[0][0] == DBNull.Value) ? 0: DataContainer.Tables[RightsBitmask].Rows[0][0]);
			}	
		}

		public string Institution 
		{ 
			get 
			{ 
				if ( DataContainer.Tables[PIInstitution] == null ) 
				{
					Utility.DataAccess.LoadDataSet(DataContainer, PIInstitution, Utility.DataAccess.GetConnectionString(), "GetUserInstitution", 
						Utility.DataAccess.BuildSqlParameterArray("@domainUserID", SqlDbType.Int, DomainUserId));

					if ( !DataContainer.Tables.Contains(PIInstitution) )
						throw new Exception("Failed to load user's Institution");
				}

				Cache();				

				return (string)(DataContainer.Tables[PIInstitution].Rows[0][PIInstitution]);
			}	
		}
       
        /// <summary>
		/// Returns the list of PI's that the logged in user can give other's access to. 
		/// </summary>
		public DataTable PIList 
		{
			get 
			{
				if ( DataContainer.Tables[PIListTableName] == null ) 
				{
					Utility.DataAccess.LoadDataSet(DataContainer, PIListTableName, Utility.DataAccess.GetConnectionString(), "GetAdminAccessPIList");

					if ( !DataContainer.Tables.Contains(PIListTableName) )
						throw new Exception("Failed to load PI list into user context.");

					Cache();
				}				
				return DataContainer.Tables[PIListTableName];
			}
		}

		/// <summary>
		/// ApplicationUserId is the user's application user id
		/// </summary>
		public int ApplicationUserId
		{ 
			get 
			{ 
				return (int)DataContainer.Tables[UserContextTableName].Rows[0][ApplicationUserIdColName];
			}	
		}

		/// <summary>
		/// 
		/// </summary>
		public DateTime CurrentSessionEndDate
		{ 
			set 
			{
				if (!DataContainer.Tables.Contains(CurrentSessionHistoryTableName))
				{
					DataTable currentSessionHistoryTable = DataContainer.Tables[SessionHistoryTableName].Clone();
					currentSessionHistoryTable.TableName = CurrentSessionHistoryTableName;
					DataContainer.Tables.Add(currentSessionHistoryTable);

					DataContainer.Tables[CurrentSessionHistoryTableName].Rows.Add(DataContainer.Tables[CurrentSessionHistoryTableName].NewRow());
					DataContainer.Tables[CurrentSessionHistoryTableName].Rows[0][SessionApplicationUserIdColName] = ApplicationUserId;
				}
				
				DataContainer.Tables[CurrentSessionHistoryTableName].Rows[0][SessionEndDateColName] = value;
			}	
		}

		/// <summary>
		/// 
		/// </summary>
		public DateTime CurrentSessionStartDate
		{ 
			set 
			{
				if (!DataContainer.Tables.Contains(CurrentSessionHistoryTableName))
				{
					DataTable currentSessionHistoryTable = DataContainer.Tables[SessionHistoryTableName].Clone();
					currentSessionHistoryTable.TableName = CurrentSessionHistoryTableName;
					DataContainer.Tables.Add(currentSessionHistoryTable);

					DataContainer.Tables[CurrentSessionHistoryTableName].Rows.Add(DataContainer.Tables[CurrentSessionHistoryTableName].NewRow());
					DataContainer.Tables[CurrentSessionHistoryTableName].Rows[0][SessionApplicationUserIdColName] = ApplicationUserId;
				}

				DataContainer.Tables[CurrentSessionHistoryTableName].Rows[0][SessionStartDateColName] = value;
			}	
		}

		/// <summary>
		/// DomainUserId is the user's domain user id
		/// </summary>
		public int DomainUserId
		{ 
			get 
			{ 
				return (int)DataContainer.Tables[UserContextTableName].Rows[0][DomainUserIdColName];
			}	
		}

		/// <summary>
		/// Username is the user's domain user username
		/// </summary>
		public string Username
		{ 
			get 
			{ 
				return (string)DataContainer.Tables[UserContextTableName].Rows[0][UsernameColName];
			}	
		}

		/// <summary>
		/// FirstName is the user's first name
		/// </summary>
		public string FirstName
		{ 
			get 
			{ 
				return (string)DataContainer.Tables[UserContextTableName].Rows[0][FirstNameColName];
			}	
		}
		
		/// <summary>
		/// LastName is the user's last name.
		/// </summary>
		public string LastName
		{ 
			get 
			{ 
				return (string)DataContainer.Tables[UserContextTableName].Rows[0][LastNameColName];
			}	
		}
		
		/// <summary>
		/// PersonId is the user's admpersonid
		/// </summary>
		public int PersonId
		{ 
			get 
			{ 
				return (int)DataContainer.Tables[UserContextTableName].Rows[0][PersonIdColName];
			}	
		}

		/// <summary>
		/// EmailAddress is the user's primary e-mail address.
		/// </summary>
		public string EmailAddress
		{
			get
			{
				object o = DataContainer.Tables[UserContextTableName].Rows[0][EmailAddressColName];
				return (o == DBNull.Value) ? string.Empty : (string)o;
			}
		}		
	
		/// <summary>
		/// 
		/// </summary>
		public int SessionApplicationUserId
		{ 
			get 
			{ 
				return (int)DataContainer.Tables[SessionHistoryTableName].Rows[0][SessionApplicationUserIdColName];
			}	
			set 
			{ 
				if (DataContainer.Tables[SessionHistoryTableName].Rows.Count == 0)
					DataContainer.Tables[SessionHistoryTableName].Rows.Add(DataContainer.Tables[SessionHistoryTableName].NewRow());

				DataContainer.Tables[SessionHistoryTableName].Rows[0][SessionApplicationUserIdColName] = value;
			}	
		}

		/// <summary>
		/// 
		/// </summary>
		public DateTime SessionEndDate
		{ 
			get 
			{ 
				return (DateTime)DataContainer.Tables[SessionHistoryTableName].Rows[0][SessionEndDateColName];
			}	
		}

		/// <summary>
		/// 
		/// </summary>
		public DateTime SessionStartDate
		{ 
			get 
			{ 
				return (DateTime)DataContainer.Tables[SessionHistoryTableName].Rows[0][SessionStartDateColName];
			}	
		}
		#endregion

		#region BusinessObject Methods
		/// <summary>Caches thhis object based on it's caching policy.</summary>
		public override void Cache()
		{
			CachePolicy.Cache(CacheKey, this);
		}

		/// <summary>UnCaches an object based on it's caching policy and a specified key.</summary>
		public override void UnCache()
		{
			CachePolicy.UnCache(CacheKey);
		}

		/// <summary>
		/// Load the DataSet with UserContext data by executing the stored procedure
		/// "GetPerson".  Throws system exception if an error is encountered 
		/// during the load process.
		/// </summary>
		/// <param name="connectionString">Sql Connection string info</param>
		public override void Load(string connectionString, params object[] args) 
		{
			if (args.Length != 1)
				throw new Exception("UserContext Load Method expects 1 argument");

			domainUserName = (string)args[0];
			DataContainer.Reset();

			Utility.DataAccess.LoadDataSet(DataContainer, UserContextTableName, connectionString, "GetUserContext", 
				Utility.DataAccess.BuildSqlParameterArray("@userName" ,SqlDbType.VarChar, domainUserName));

			if (DataContainer.Tables[UserContextTableName].Rows.Count == 0) 
			{
				throw new Exception(string.Format("{0} is not a valid Insight user", domainUserName));
			}

			Utility.DataAccess.LoadDataSet(DataContainer, SessionHistoryTableName, connectionString, "GetUsersMostRecentSessionHistory", 
				Utility.DataAccess.BuildSqlParameterArray("@userName" ,SqlDbType.VarChar, domainUserName));
		}

		/// <summary>
		/// Saving inserts new rows into the database based on newly added rows
		/// in the DataTables and updates rows in the database based on
		/// rows that have been updated in the DataTables.
		/// </summary>
		/// <param name="connectionString">Connection string to the database</param>
		public override void Save(string connectionString, params object[] args)
		{
			if (Utility.Settings.GetKeyValue("SaveLoginHistory", false))
			{
				SqlConnection connection = new SqlConnection(connectionString);
				try
				{
					connection.Open();
					addCommand.Connection = updateCommand.Connection = connection;
				
					SqlDataAdapter adapter = new SqlDataAdapter();
					adapter.InsertCommand = addCommand;
					adapter.UpdateCommand = updateCommand;
					adapter.Update(DataContainer, CurrentSessionHistoryTableName);
				}
				finally
				{
					if (connection.State != ConnectionState.Closed)
						connection.Close();
				}
			}
		}
		#endregion

		#region Public Methods
		public bool IsFirstTimeUser()
		{
			return (DataContainer.Tables[SessionHistoryTableName].Rows.Count == 0);
		}

        /// <summary>
        /// Gets whether or not to prompt the logged in user to hit search when landing on pages in the application.
        /// </summary>
        public bool ShouldPromptForResourceSearch(string moduleName)
        {
            return (GetOrganizationAccessCount(moduleName) > Utility.Settings.GetKeyValue("MaxOrgAccessWithoutSearchPrompt", 10));
        }

        /// <summary>
        /// Gets whether or not to prompt the logged in user to hit search when landing on 
        /// protocol pages in the application.
        /// </summary>
        public bool ShouldPromptForProtocolSearch(string moduleName)
        {
            return (GetPortfolioAccessCount(moduleName) > Settings.GetKeyValue("MaxPortfolioAccessWithoutSearchPrompt", 5)
                    || ShouldPromptForResourceSearch(moduleName));
        }

        /// <summary>
        /// Gets the number of organizations the user has access to via organization access rights.
        /// </summary>
        public int GetOrganizationAccessCount(string moduleName)
        {
            return User.GetOrganizationRights(0).GetOrganizationAccessCount(moduleName);
        }

        /// <summary>
        /// Gets the number of portfolios the user has access to via portfolio access rights.
        /// </summary>
        public int GetPortfolioAccessCount(string moduleName)
        {
            // Not filtering by a particular PI
            return User.GetPortfolioRights(0).GetPortfolioAccessCount(moduleName);
        }
        #endregion

		#region Private Methods
		/// <summary>
		/// Create the set of SqlCommand objects used for the insertion, deletion, and updates made on
		/// the associated DataTable. Notice that each command is initialized to invoke a stored
		/// procedure implying that each command may have its own set of parameters in line with that
		/// stored procedure.
		/// </summary>
		private void CreateCommands()
		{
			addCommand = new SqlCommand();
			addCommand.CommandText = "AddUserSessionHistory";
			addCommand.CommandType = CommandType.StoredProcedure;
			addCommand.CommandTimeout = Convert.ToInt32(Utility.Settings.GetKeyValue("Database.Insight.CommandTimeout", "0"));
			addCommand.Parameters.Add("@applicationUserId", SqlDbType.Int).SourceColumn = SessionApplicationUserIdColName;
			addCommand.Parameters.Add("@sessionStartDate", SqlDbType.DateTime).SourceColumn = SessionStartDateColName;

			updateCommand = new SqlCommand();
			updateCommand.CommandText = "UpdateUserSessionHistory";
			updateCommand.CommandType = CommandType.StoredProcedure;
			updateCommand.CommandTimeout = Convert.ToInt32(Utility.Settings.GetKeyValue("Database.Insight.CommandTimeout", "0"));
			updateCommand.Parameters.Add("@sessionHistoryId", SqlDbType.Int).SourceColumn = SessionHistoryIdColName;
			updateCommand.Parameters.Add("@sessionEndDate", SqlDbType.DateTime).SourceColumn = SessionEndDateColName;
		}
		#endregion
	}
	#endregion
}

// --- EOF ---
